﻿using System;

namespace HtmlParser
{
	class HtmlByteAnalyzer
	{

		private static HtmlTagType getTagType(ref string tagName)
		{
			if (String.IsNullOrEmpty(tagName)) return HtmlTagType.Unknown;
			tagName = tagName.ToLower();

			if (tagName[0] == '!')
			{
				if (tagName.Length > 2)
				{
                    if (tagName[1] == '-')
                    {
                        tagName = "#comment";
                        return HtmlTagType.Comment;
                    }
                    else if (tagName.ToUpper() == "!DOCTYPE")
                    {
                        tagName = "#doctype";
                        return HtmlTagType.DocType;
                    }
                    else
                    {
                        tagName = "#unknown";
                        return HtmlTagType.Unknown;
                    }
				}
				else
				{
                    tagName = "#unknown";
                    return HtmlTagType.Unknown;
				}
			}
			else if (tagName[0] == '/' && tagName.Length > 1)
			{
				tagName = tagName.Trim('/', ' ');
				return HtmlTagType.TagEnd;
			}
			else if (tagName[tagName.Length - 1] == '/' && tagName.Length > 1)
			{
				tagName = tagName.Trim('/', ' ');
				return HtmlTagType.TagSingle;
			}
			else if (tagName == "script" || tagName == "style")
			{
				return HtmlTagType.Script;
			}
			return HtmlTagType.Tag;
		}
		public static HtmlTagCollection ReadHtmlElement(string s, HtmlTagTokenCollection tokens)
		{
			var reader = new ByteStringReader(s);

			var tags = new HtmlTagCollection(s);

			int i0 = 0, i1, peek;

			HtmlTagType tagType;
			string tagName;

			while (!reader.EOF)
			{

				i1 = reader.To('<');
                if (i1 > -1)
                {
                    peek = reader.Peek();
                    if (!HtmlTagToken.IsTagNameFirst(peek))
                    {
                        continue;
                    }
                }
                else
                {
                    i1 = reader.Length - 1;
                    if (i0 > i1)
                    {
                        break;
                    }
                    tags.Add("#text", HtmlTagType.Text, new int[] { i0, i1 });
                    i0 = i1;
                    break;
                }
                

				if (i1 - i0 > 0)
				{
					tags.Add("#text", HtmlTagType.Text, new int[] { i0, i1 - 1 });
				}

				tagName = reader.PeekTagName();

				if (tagName == null) continue;
				tagType = getTagType(ref tagName);

				reader.Back();
				var tag = reader.Read(tokens);

				if (tag != null)
				{
					i0 = tag[0];
					if (tagType == HtmlTagType.Comment)
					{
						if (reader.PeekBack() == '-')
						{
							i1 = tag[1];
						}
						else
						{
							var i = reader.To(delegate (int c)
							{
								return c == '>' && reader.PeekBack() == '-';
							});
							if (i < 0)
							{
								i1 = reader.Length - 1;
							}
							else
							{
								i1 = i;
							}
						}
						tags.Add(tagName, tagType, new int[] { i0, i1 });
						i0 = i1 + 1;
					}
					else
					{
						i1 = tag[1];
						tags.Add(tagName, tagType, new int[] { i0, i1 });
						i0 = i1 + 1;

						if (tagType == HtmlTagType.Script)
						{
							var endTag = "</" + tagName;
							i1 = reader.To(endTag);
							if (i1 < 0)
							{
								i1 = reader.Length - 1;
								tags.Add(tagName, HtmlTagType.Text, new int[] { i0, i1 });
								break;
							}
							else
							{
								tags.Add(tagName, HtmlTagType.Text, new int[] { i0, i1 });
								reader.Back();
								i0 = i1 + 1;
							}
						}
					}
				}
				else
				{
					break;
				}
			}
			if (i0 < reader.Length - 1)
			{
				tags.Add("#text", HtmlTagType.Text, new int[] { i0, reader.Length - 1 });
			}
			return tags;
		}
    }
}
